using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;

namespace Dida
{
	/// <summary>
	/// A 2D rectangle.
	/// </summary>
	[DebuggerDisplay("({min.x}, {min.y}) - ({max.x}, {max.y})")]
	public struct Rect
	{
		public Vector2D min;
		public Vector2D max;

		/// <summary>
		/// Creates a rectangle, initialized with the given parameters.
		/// </summary>
		/// <param name="minx">The initial x component of the <see cref = "min"/> corner.</param>
		/// <param name="miny">The initial x component of the <see cref = "min"/> corner.</param>
		/// <param name="maxx">The initial y component of the <see cref = "max"/> corner.</param>
		/// <param name="maxy">The initial y component of the <see cref = "max"/> corner.</param>
		public Rect(float minx, float miny, float maxx, float maxy)
		{
			min = new Vector2D(minx, miny);
			max = new Vector2D(maxx, maxy);
		}

		/// <summary>
		/// Creates a rectangle, initialized with the given parameters.
		/// </summary>
		/// <param name="min">The initial <see cref = "min"/> corner.</param>
		/// <param name="max">The initial <see cref = "max"/> corner.</param>
		public Rect(Vector2D min, Vector2D max)
		{
			this.min = min;
			this.max = max;
		}

		/// <summary>
		/// Gets or sets the width of this rectangle.
		/// </summary>
		/// <remarks>
		/// If the width is changed using this property, 
		/// the max corner will be changed accordingly. The min corner
		/// will remain at it's current position.
		/// </remarks>
		public float Width
		{
			get { return max.x - min.x; }
			set { max.x = min.x + value; }
		}

		/// <summary>
		/// Gets or sets the height of this rectangle.
		/// </summary>
		/// <remarks>
		/// If the height is changed using this property, 
		/// the max corner will be changed accordingly. The min corner
		/// will remain at it's current position.
		/// </remarks>
		public float Height
		{
			get { return max.y - min.y; }
			set { max.y = min.y + value; }
		}

		public Vector2D Center
		{
			get
			{
				return (min + max) * .5f;
			}
		}

		public Vector2D Size
		{
			get
			{
				return max - min;
			}
		}

		public static Rect NullRect
		{
			get
			{
				return new Rect(
					float.MaxValue, float.MaxValue,
					-float.MaxValue, -float.MaxValue);
			}
		}

		public void Union(Rect b)
		{
			if (b.min.x < min.x)
				min.x = b.min.x;
			if (b.min.y < min.y)
				min.y = b.min.y;
			if (b.max.x > max.x)
				max.x = b.max.x;
			if (b.max.y > max.y)
				max.y = b.max.y;
		}

		public void Expand(Vector2D pt)
		{
			if (pt.x < min.x)
				min.x = pt.x;
			if (pt.x > max.x)
				max.x = pt.x;
			if (pt.y < min.y)
				min.y = pt.y;
			if (pt.y > max.y)
				max.y = pt.y;
		}

		/// <summary>
		/// Returns whether the given point lays in this rectangle.
		/// </summary>
		/// <param name="pt">The point you want to test.</param>
		/// <returns>True when the point lays inside the rect, false otherwise.</returns>
		public bool PointInRect(Vector2D pt)
		{
			return pt.x > min.x && pt.x < max.x &&
				pt.y > min.y && pt.y < max.y;
		}

		public bool Intersects(Rect b)
		{
			return IntersectionTests.RectRect(this, b);
		}

		/// <summary>
		/// Returns a rect that's the intersection between this rect and the rect in b.
		/// </summary>
		/// <param name="b">One of the 2 rectangles that should intersect. (The other
		/// one is this rectangle.</param>
		/// <param name = "res">The rectangle that will receive the intersection.</param>
		/// <remarks>True if the operation was successful, false if the rectangles
		/// didn't intersect at all, and so an intersection rectangle couldn't be 
		/// calculated. If the result was false, the value placed in res is undefined.</remarks>
		public bool GetIntersectionRect(Rect b,out Rect res)
		{
			res = new Rect(
				Math.Max(min.x, b.min.x),
				Math.Max(min.y, b.min.y),
				Math.Min(max.x, b.max.x),
				Math.Min(max.y, b.max.y));
			bool success = res.max.x >= res.min.x && res.max.y >= res.min.y;
			Debug.Assert(success == Intersects(b), "The success result of this " + 
				"function should be the same as the result of the Intersects " + 
				"method between the two rectangles");
			return success;
		}

		/// <summary>
		/// Returns the distance between the given point, and the 
		/// closest point in this rectangle.
		/// </summary>
		/// <param name="pt">The point to which you want to get the distance.</param>
		/// <returns>The distance.</returns>
		public float GetDistToPoint(Vector2D pt)
		{
			float sqrDist = 0;
			for (int i = 0; i < 2; i++)
			{
				if(pt[i] < min[i])
				{
					float axisDist = pt[i] - min[i];
					sqrDist += axisDist * axisDist;
				}
				else if (pt[i] > max[i])
				{
					float axisDist = pt[i] - max[i];
					sqrDist += axisDist * axisDist;
				}
			}
			return (float)Math.Sqrt(sqrDist);
		}
	}
}
